<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <link href=""
        rel="icon" type="image/x-icon" />
    <script>
        (function (Pipe, perf) {
            if (Pipe === undefined) {
                return;
            }
            if (!('mark' in perf && 'measure' in perf)) {
                return;
            }
            var fragmentMap = Object.create(null);
            var doneGroups = Object.create(null);

            function getCount(range) {
                return range[1] - range[0] + 1;
            }
            /**
             * Elapsed time from browsers navigation start
             */
            function elapsedTime() {
                return perf.now() || Date.now() - perf.timing.navigationStart;
            }

            function addGroupEntries() {
                for (var name in doneGroups) {
                    var duration = doneGroups[name];
                    if (duration !== undefined) {
                        Pipe.addPerfEntry(name, duration);
                    }
                }
            }

            /**
             * Group all the timing groups associated with all fragments
             * and check if the scripts are done executing
             *
             * If there are any fragment that gets lazily rendered then we need to
             * account for the scripts from that fragment as well
             *
             * Measure -> time from navigation start - all scripts from
             * fragments associated with given timing groups are done executing
             */
            function measureGroups(groups) {
                if (groups.length < 1) {
                    return;
                }
                for (var i = 0; i < groups.length; i++) {
                    var groupName = groups[i];
                    var isGroupDone = true;
                    var keys = Object.keys(fragmentMap);
                    for (var j = 0; j < keys.length; j++) {
                        var fragment = fragmentMap[keys[j]];
                        if (
                            fragment.groups.indexOf(groupName) >= 0 &&
                            fragment.count > 0
                        ) {
                            isGroupDone = false;
                            break;
                        }
                    }

                    if (isGroupDone) {
                        // Keep updating the timings for the group to account for
                        // fragments that are streamed later
                        doneGroups[groupName.trim()] = elapsedTime();
                    }
                }
            }

            Pipe.onStart(function (attributes) {
                var id = attributes.id;
                if (fragmentMap[id] === undefined) {
                    var count = getCount(attributes.range);
                    /*
                     * count - denotes the number of script tags
                     * marked - flag to check if fragment started executing
                     * measure - flag to measure sripts and fragments differently
                     * groups - timing groups associated with a fragment
                     */
                    fragmentMap[id] = {
                        count: count,
                        marked: false,
                        measure: count !== 1,
                        groups: attributes.timingGroups
                    };
                }
            });
            Pipe.onBeforeInit(function (attributes, index) {
                var id = attributes.id;
                var fragment = fragmentMap[id];
                /**
                 * Mark only once even if fragments send multiple scripts
                 * Helps to capture the overall time from start till all the
                 * scripts are executed from fragment
                 *
                 * Includes network time of other scripts as well.
                 */
                if (!fragment.marked) {
                    fragment.marked = true;
                    perf.mark(id);
                }
                /**
                 * Mark for each script tag from the fragments
                 * Helps us to track how much time is spent executing
                 * each script on the fragments
                 *
                 * Includes only the execution time of the exported function
                 * in the script
                 */
                if (fragment.measure) {
                    perf.mark(id + index);
                }
            });
            Pipe.onAfterInit(function (attributes, index) {
                var id = attributes.id;
                var timingGroups = attributes.timingGroups;
                var fragment = fragmentMap[id];
                // Measure for each script per fragment only if
                // the script count is more than 1 per fragment
                var markStart = '',
                    markEnd = '',
                    measureName = '';
                if (fragment.measure) {
                    markStart = id + index;
                    markEnd = markStart + 'end';
                    measureName = 'fragment-' + id + fragment.count;
                    perf.mark(markEnd);
                    perf.measure(measureName, markStart, markEnd);
                }
                fragment.count -= 1;
                /**
                 * Measures from the first script tag execution to the last one
                 * for a single fragment
                 *
                 * fragment-{name} -> start to end
                 */
                if (fragment.count === 0) {
                    markStart = id;
                    markEnd = markStart + 'end';
                    measureName = 'fragment-' + id;
                    perf.mark(markEnd);
                    perf.measure(measureName, markStart, markEnd);
                    // Measure when primary fragment is done
                    if (attributes.primary) {
                        perf.measure('primary-done');
                    }
                    // Measure if fragments assosiated with given timing groups are done
                    measureGroups(timingGroups);
                }
            });
            Pipe.onDone(function () {
                /**
                 * Create performance entries for all the collected metrics from
                 * timing groups
                 */
                addGroupEntries();
                /**
                 * Measure when all the script tags from fragments
                 * are done executing on the page
                 */
                perf.measure('all-done');
            });
            Pipe.onDone(() => {
                perf.measure('all-done-2');
            });
        })(window.TailorPipe, window.performance);

    </script>
    <script>
        define('js1', function () {
            return 'js1';
        });
        define('js2', function () {
            return 'js2';
        });
        define('js3', function () {
            return 'js3';
        });

    </script>
</head>

<body>
    <div>
        <h2>Header:</h2>
        <fragment timing-group="abovethefold,interactive" id="header" src="http://localhost:8081"></fragment>
        <h2>Product/Primary:</h2>
        <fragment timing-group="interactive" id="product" primary src="http://localhost:8082"></fragment>
        <h2>Async Footer:</h2>
        <fragment id="footer" timing-group="belowthefold" async src="http://localhost:8083"></fragment>
    </div>
</body>

</html>
